package dcutils.rpg.items;

// Import Java JDK Classes
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

// Import DCUtils Classes
import dcutils.rpg.currency.Currency;
import dcutils.tuples.*;

/**
 * This class is designed to return item objects based on user-defined database schemas.
 * @author dca
 */
public final class ItemFactory {
   private ItemFactory() {} // END constructor

   //--------------------
   // create item methods
   //--------------------

   public static Item createItem(String itemName, String itemTypeName) {
      return new Item(itemName, new ItemType(itemTypeName));
   } // END createItem

   public static Item createItem(String itemName, String itemTypeName, Currency itemValue) {
      return new Item(itemName, new ItemType(itemTypeName), itemValue);
   } // END createItem

   public static Item createItem(int itemId, int itemTypeId, String itemName, String itemTypeName) {
      return new Item(itemId, itemName, new ItemType(itemTypeId, itemTypeName));
   } // END createItem

   public static Item createItem(int itemId, int itemTypeId, String itemName, String itemTypeName, Currency itemValue) {
      return new Item(itemId, itemName, new ItemType(itemTypeId, itemTypeName), itemValue);
   } // END createItem

   // ---------------------
   // load ItemType methods
   // ---------------------

   public static ItemType loadItemTypeFromDB(
      Connection conn,
      String query,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final List<ItemType> list = loadItemTypesFromDB(conn, query, itemTypeFields);

      if(1 != list.size()) {
         throw new SQLException(String.format("Expected 1 result.  Instead, received %d.", list.size()));
      } else {
         return list.get(0);
      } // END if/else
   } // END loadItemTypesFromDB

   public static List<ItemType> loadItemTypesFromDB(
      Connection conn,
      String query,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final PreparedStatement stmt = conn.prepareStatement(query);
      return loadItemTypesFromDB(stmt, itemTypeFields);
   } // END loadItemTypesFromDB

   public static List<ItemType> loadItemTypesFromDB(
      PreparedStatement stmt,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final List<ItemType> list = new ArrayList<ItemType>();
      final ResultSet data = stmt.executeQuery();

      while(data.next()) {
         final int itemTypeId      = data.getInt(itemTypeFields.first());
         final String itemTypeName = data.getString(itemTypeFields.second());

         list.add(new ItemType(itemTypeId, itemTypeName));
      } // END data loop

      return list;
   } // END loadItemTypesFromDB

   // -----------------
   // load Item methods
   // -----------------

   public static Item loadItemFromDB(
      Connection conn,
      String query,
      Triplet<String, String, String> itemFields,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final List<Item> list = loadItemsFromDB(conn, query, itemFields, itemTypeFields);

      if(1 != list.size()) {
         throw new SQLException(String.format("Expected 1 result.  Instead, received %d.", list.size()));
      } else {
         return list.get(0);
      } // END if/else
   } // END loadItemFromDB

   public static List<Item> loadItemsFromDB(
      Connection conn,
      String query,
      Triplet<String, String, String> itemFields,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final PreparedStatement stmt = conn.prepareStatement(query);
      return loadItemsFromDB(stmt, itemFields, itemTypeFields);
   } // END loadItemsFromDB

   public static List<Item> loadItemsFromDB(
      PreparedStatement stmt,
      Triplet<String, String, String> itemFields,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final List<Item> list = new ArrayList<Item>();
      final ResultSet data = stmt.executeQuery();

      while(data.next()) {
         final int itemId          = data.getInt(itemFields.first());
         final String itemName     = data.getString(itemFields.second());
         final int itemTypeId      = data.getInt(itemTypeFields.first());
         final String itemTypeName = data.getString(itemTypeFields.second());
         final int itemValue       = data.getInt(itemFields.third());

         list.add(
            createItem(
               itemId,
               itemTypeId,
               itemName,
               itemTypeName,
               (data.wasNull() ? null : new Currency(itemValue))
            )
         );
      } // END data loop

      return list;
   } // END loadItemsFromDB

   // ----------------------
   // load Inventory methods
   // ----------------------

   public static Inventory loadInventoryFromDB(
      Connection conn,
      String query,
      String numField,
      Triplet<String, String, String> itemFields,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final PreparedStatement stmt = conn.prepareStatement(query);
      return loadInventoryFromDB(stmt, numField, itemFields, itemTypeFields);
   } // END loadInventoryFromDB

   public static Inventory loadInventoryFromDB(
      PreparedStatement stmt,
      String numField,
      Triplet<String, String, String> itemFields,
      Pair<String, String> itemTypeFields
   ) throws SQLException {
      final Inventory inv = new Inventory();
      final ResultSet data = stmt.executeQuery();

      while(data.next()) {
         final int itemId          = data.getInt(itemFields.first());
         final String itemName     = data.getString(itemFields.second());
         final int itemValue       = data.getInt(itemFields.third());
         final int itemTypeId      = data.getInt(itemTypeFields.first());
         final String itemTypeName = data.getString(itemTypeFields.second());
         final int num             = data.getInt(numField);

         // Check if num field was null (this means infinite number.)
         inv.addItem(
            createItem(
               itemId,
               itemTypeId,
               itemName,
               itemTypeName,
               (data.wasNull() ? null : new Currency(itemValue))
            ),
            (data.wasNull() ? null : num)
         );
      } // END data loop

      return inv;
   } // END loadInventoryFromDB
} // END class ItemFactory
